import time

from pymongo import MongoClient
import random
import numpy as np
import pandas as pa

client = MongoClient('localhost', 27017)
db = client['learning']
collection1 = db['users']
collection2 = db['behavior']
collection3 = db['cold']
collection4 = db['hell']
collection3.create_index([('username', 1), ('partner', 1)])
collection4.create_index([('username', 1), ('partner', 1)])
flag = ['initialLevel', 'learnPurpose', 'planning', 'learnStyle']
dimension = ['motivation', 'attitude', 'efficiency', 'perseverance']
friend_net = pa.read_csv('./Flixster-dataset/data/edges.csv', header=None, sep=',')


def make_user():
    user_list = collection1.find({'role': 'user'})
    member_list = []
    user = []
    total = []
    for i in user_list:
        member_list.append(i)
    for member in member_list:
        data = []
        user.append(member['username'])
        for name in flag:
            data.append(member[name])
        total.append(data)
    return user, total


def max_min(target, index):
    min_number = min(target)
    max_number = max(target) if min_number != max(target) else min_number + 1
    return pa.Series(np.array(list(map(lambda number: float(format((number - min_number)
                                                                   / (max_number - min_number), '.1f')),
                                       target))), index=index)


# 计算欧式距离前一般做这个，并且不知道最值
def z_score(target, index):
    av = np.mean(target)
    std = np.std(target, ddof=1) if np.std(target, ddof=1) != 0 else 1
    return pa.Series(np.array(list(map(lambda number: float(format((number - av) / std, '.1f')), target))), index=index)


# 0-无 1 差 2 中 3 良 4 优
def generate_data(n):
    target = list()
    for number in range(n):
        target.append(random.randint(0, 4))
    return np.array(target)


class LearnModel:
    motivation_rate = np.array([0.2, 0.2, 0.3, 0.3])
    attitude_rate = np.array([0.5, 0.5])
    efficiency_rate = np.array([0.4, 0.3, 0.3])
    perseverance_rate = np.array([0.4, 0.1, 0.5])

    def __init__(self, username):
        self.motivation_list = generate_data(4)
        self.attitude_list = generate_data(2)
        self.efficiency_list = generate_data(3)
        self.perseverance_list = generate_data(3)
        self.username = username

    def make_list(self):
        motivation = np.sum(self.motivation_list * self.motivation_rate)
        attitude = np.sum(self.attitude_list * self.attitude_rate)
        efficiency = np.sum(self.efficiency_list * self.efficiency_rate)
        perseverance = np.sum(self.perseverance_list * self.perseverance_rate)
        return list(map(lambda number: float(format(number, '.1f')), [motivation,
                                                                      attitude, efficiency, perseverance]))


def fill_table(user):
    for item in user:
        if collection2.find_one({'username': item}) is None:
            model = LearnModel(item)
            data_list = model.make_list()
            info = dict()
            info['username'] = model.username
            for pointer in range(4):
                info[dimension[pointer]] = data_list[pointer]
            collection2.insert_one(info)


def fill_it(user, answer, collection):
    for my_name in range(len(user)):
        for partner_name in answer[my_name]:
            if collection.find_one({'username': user[my_name], 'partner': partner_name}) is None:
                collection.insert_one({'username': user[my_name], 'partner': partner_name,
                                       'random': random.random()})


def cold_start(user, table, friends):
    name_list = list()
    for it in user:
        if collection3.find_one({'username': it}) is None:
            name_list.append(it)
    # if len(name_list) == 0:
    #     return
    # answer = table.pipe(np.corrcoef).astype(float)
    answer = table.T.corr('pearson')
    ha = list()
    special = answer.isna().all(1)
    for it in name_list:
        recommends = list()
        index = user.index(it)
        number_set = answer[it].drop_duplicates().tolist()
        number = [x for x in number_set if x == x]
        number = sorted(number)[-3:]
        if it in special[special].index:
            father_list = list(special[special].index)
            father_list.remove(it)
            for i in father_list:
                if table.loc[i, 'initialLevel'] == table.loc[it, 'initialLevel']:
                    recommends.append(i)
        if len(number) <= 1:
            condition = friends[friends[0] == user.index(it) + 1]
            if len(condition) != 0:
                for father_name in condition[1]:
                    recommends.append(user[father_name - 1])
            else:
                if len(recommends) == 0:
                    recommends = user[:]
                else:
                    recommends += user[:]
                recommends.remove(it)
        else:
            for its in range(len(answer[it])):
                if answer.iloc[index][its] in number and its != index:
                    recommends.append(user[its])
        ha.append(recommends)
    fill_it(name_list, ha, collection3)


def user_cf(user, friends):
    data_list = []
    for pointer in collection2.find({}):
        data_list.append(pointer)
    table_data = list()
    for pointer in data_list:
        every_data = list()
        for its_name in dimension:
            every_data.append(pointer[its_name])
        table_data.append(every_data)
    data_table = pa.DataFrame(table_data, index=user, columns=dimension)
    final_data = data_table.apply(max_min, axis=1, args=(dimension,))
    # answer = final_data.pipe(np.corrcoef)
    answer = final_data.T.corr('pearson')
    haha = list()
    special = answer.isna().all(1)
    for pointer in range(len(user)):
        # number_set = sorted(list(set(answer[pointer])))
        name_list = list()
        number_set = answer[user[pointer]].drop_duplicates().tolist()
        number = [x for x in number_set if x == x]
        number = sorted(number)[-3:]
        # print(number)
        if user[pointer] in special[special].index:
            father_list = list(special[special].index)
            father_list.remove(user[pointer])
            for i in father_list:
                if final_data.loc[i, 'initialLevel'] == final_data.loc[user[pointer], 'initialLevel']:
                    name_list.append(i)
        if len(number) <= 1:
            condition = friends[friends[0] == pointer + 1]
            if len(condition) == 0:
                if len(name_list) == 0:
                    name_list = user[:]
                else:
                    name_list += user[:]
                name_list.remove(user[pointer])
            else:
                for bili in condition[1]:
                    name_list.append(user[bili - 1])

        else:
            score = number
            for index in range(len(user)):
                if answer.iloc[pointer][index] in score and index != pointer:
                    name_list.append(user[index])
            condition = friends[friends[0] == pointer + 1]
            if len(condition) != 0:
                for nlp in condition[1]:
                    if answer.iloc[pointer][nlp - 1] > 0 and user[nlp - 1] not in name_list:
                        name_list.append(user[nlp - 1])
        haha.append(name_list)
    fill_it(user, haha, collection4)


# def handle_nan(data, table, answer):
#
#     answer = table.isna().all(1)
#     print('NFnDJl4s' in answer[answer].index)


if __name__ == '__main__':
    while True:
        start = time.time()
        user_name, total_data = make_user()
        if len(user_name) != 0:
            print('start')
            # 提取静态数据
            table1 = pa.DataFrame(np.array(total_data), index=user_name, columns=flag)
            # 提取朋友数据集
            friend = friend_net[(friend_net[0] <= len(user_name)) & (friend_net[1] <= len(user_name))]
            table2 = table1.apply(max_min, axis=1, args=(flag,))
            fill_table(user_name)
            cold_start(user_name, table2, friend)
            end = time.time()
            print(end - start)
            user_cf(user_name, friend)
            # break
    print("over")
